// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
// ANY KIND, either express or implied. See the License for the specific terms governing rights and
// limitations under the License.
//
// This code was produced as part of the WaterBase project: http://www.waterbase.org
//
// Original author: Chris George, 2/13/2008
//

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Globalization;

namespace SWATPlot
{
	/// <summary>
	/// Description of MainForm.
	/// </summary>
	public partial class MainForm : Form
	{
		// The following constants are also found in mwSWATResults.cs
		// Both files should be changed in the same way.
		// common to all SWAT output files
		private const int headerLines = 8;
		// column numbers start from 0
		// so if a field starts in column c
		// and has width w
		// then a field value in a line l is obtained from the string
		// l.Substring(c, w)
		private const int dateWidth = 4;
		private const string figFile = @"fig.fig";
		private const int figCommandCol = 10;
		private const int figCommandWidth = 6;
		private const int figResCol = 22;
		private const int figResWidth = 6;
		private const int figRouteBasinCol = 22;
		private const int figRouteBasinWidth = 6;
		private const string reachDataFile = @"output.rch";
		private const int reachFirstDataCol = 37;
		private const int reachDataWidth = 12;
		private const int reachNumCol = 5;
		private const int reachNumWidth = 5;
		private const int reachDateCol = 21;
		private const string subDataFile = @"output.sub";
		private const int subFirstDataCol = 34;
		private const int subDataWidth = 10;
		private const int subNumCol = 6;
		private const int subNumWidth = 4;
		private const int subDateCol = 20;
		private const string hruDataFile = @"output.hru";
		private int hruFirstDataCol = 42;
		private const int hruDataWidth = 10;
		private int hruNumCol = 4;
		private const int hruNumWidth = 4;
		private int hruSubNumCol = 17;
		private const int hruSubNumWidth = 5;
		private int hruDateCol = 28;
		private const string waterDataFile = @"output.wtr";
		private int waterFirstDataCol = 42;
		private const int waterDataWidth = 10;
		private int waterNumCol = 4;
		private const int waterNumWidth = 4;
		private int waterSubNumCol = 17;
		private const int waterSubNumWidth = 5;
		private int waterDateCol = 28;
		private const string reservoirDataFile = @"output.rsv";
		private const int reservoirFirstDataCol = 19;
		private const int reservoirDataWidth = 12;
		private const int reservoirNumCol = 9;
		private const int reservoirNumWidth = 5;
		private const int reservoirDateCol = 15;
		
		// where SWATPlot is running from.  Used to find SWATGraph
		private string appDir; 
		// where configuration file is stored
		private string configDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\MapWindow";
		private string configFile = "SWATPlot.cfg";
		private Dictionary<string, string> scenObs = new Dictionary<string, string>();
		
		private string scenariosDir = "";
		private string observedDataFile = "";
		private List<string> reachVars = new List<string>();
		private List<string> subVars = new List<string>();
		private List<string> hruVars = new List<string>();
		private List<string> waterVars = new List<string>();
		private List<string> reservoirVars = new List<string>();
		private List<string> observedVars = new List<string>();
		private int observedStart;
		private Dictionary<string, Basins> runs;
		
		private int IPRINT; // SWAT variable read from file.cio.  0 means month interval, 1 day and 2 year
		private int NYSKIP; // SWAT variable read from file.cio
		private int startYear;
		private int startDay;
		private int requestedStartYear;
		private int requestedStartDay;
		private int finishYear;
		private int finishDay;
		private int requestedFinishYear;
		private int requestedFinishDay;
		private System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SWATPlot.MainForm));
		
		public MainForm()
		{
			//
			// The InitializeComponent() call is required for Windows Forms designer support.
			//
			InitializeComponent();
			appDir =
				Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
			PutMonths(startMonthBox);
			PutMonths(finishMonthBox);
			readConfig();
		}
		
		void readConfig()
		{
			if (!System.IO.Directory.Exists(configDir)) System.IO.Directory.CreateDirectory(configDir);
			string configPath = configDir + @"\" + configFile;
			if (System.IO.File.Exists(configPath))
			{
				System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
				doc.Load(configPath);
				
				System.Xml.XmlNodeList scenarios = doc.DocumentElement.FirstChild.ChildNodes;
				for (int i = 0; i < scenarios.Count; i++)
				{
					string scenario = scenarios[i].Attributes["Path"].Value;
					if (System.IO.Directory.Exists(scenario))
						scenariosComboBox.Items.Add(scenario);
					string observed = scenarios[i].Attributes["Observed"].Value;
					if (!System.IO.File.Exists(observed)) observed = "";
					if (!scenObs.ContainsKey(scenario))
						scenObs.Add(scenario, observed);
				}
			}
		}
		
		void scenarioChanged(object sender, EventArgs e)
		{
			scenariosComboBox.Text = (string) scenariosComboBox.SelectedItem;
			scenariosDir = scenariosComboBox.Text;
			//MessageBox.Show("Scenarios folder is " + scenariosDir);
			setScenario();
		}
		
		void projectFileClick(object sender, EventArgs e)
		{
			if ((scenariosComboBox.Text != null) && System.IO.Directory.Exists(scenariosComboBox.Text))
				scenariosFolderDialog.SelectedPath = scenariosComboBox.Text;
			if (scenariosFolderDialog.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) return;
			scenariosDir = scenariosFolderDialog.SelectedPath;
			scenariosComboBox.Text = scenariosDir;
			setScenario();
		}
		
		void setScenario()
		{
			if (!System.IO.Directory.Exists(scenariosDir)) return;
			System.IO.DirectoryInfo scenariosInfo = new System.IO.DirectoryInfo(scenariosDir);
			System.IO.DirectoryInfo [] dirs = scenariosInfo.GetDirectories();
			int dirCount = dirs.Length;
			if (dirCount == 0)
			{
				MessageBox.Show(strings.noruns + scenariosDir,
				                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}
			runs = new Dictionary<string, Basins>();
			runBox.Items.Clear(); // in case new project is selected
			bool defaultFound = false;
			for (int i = 0; i < dirCount; i++)
			{
				string dir = dirs[i].Name;
				if (dir.ToLower().Equals("observed"))
				{
					MessageBox.Show(strings.noobserved,
					                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
				else
				{
					if (dir.ToLower().Equals("default")) defaultFound = true;
				}
				Basins basins = new Basins();
				basins.hrus = new Dictionary<int, Hrus>();
				basins.reservoirs = new Dictionary<int, int>();
				basins.fiveDigitHruShift = 0;
				bool success = PopulateBasins(ref basins, dir);
				if (success)
				{
					runs.Add(dir, basins);
					runBox.Items.Add(dir);
				}
				SetSources(dir);
			}
			if (!defaultFound)
			{
				MessageBox.Show(strings.nodefaultfolder + scenariosDir,
				                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			string observed = "";
			dataFileBox.Text = observed;
			if (scenObs.ContainsKey(scenariosDir))
			{
				observed = scenObs[scenariosDir];
				observedDataFile = observed;
				dataFileBox.Text = observedDataFile;
				if (!observed.Equals(""))
				{
					dataFileDialog.InitialDirectory = System.IO.Path.GetDirectoryName(observedDataFile);
					setDataFile();
				}
			}
			InitialiseDates();
		}
		
		void dataFileClick(object sender, EventArgs e)
		{
			if (scenariosDir.Equals(""))
			{
				MessageBox.Show(strings.selectscenarios, "SWATPlot",
				                MessageBoxButtons.OK,
				                MessageBoxIcon.Error);
				return;
			}
			if (dataFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) return;
			observedDataFile = dataFileDialog.FileName;
			if (!System.IO.File.Exists(observedDataFile)) 
			{
				observedDataFile = "";
				return;
			}
			dataFileBox.Text = observedDataFile;
			if (scenObs.ContainsKey(scenariosDir)) scenObs.Remove(scenariosDir);
			scenObs.Add(scenariosDir, observedDataFile);
			setDataFile();
		}
		
		void setDataFile()
		{
			using (StreamReader sr = new StreamReader(observedDataFile))
			{
				string line = sr.ReadLine();
				if (line==null)
				{
					MessageBox.Show(strings.observedfile + observedDataFile +
					                strings.seemsempty, "SWATPlot",
					                MessageBoxButtons.OK,
					                MessageBoxIcon.Error);
					dataFileBox.Text = "";
					return;
				}
				string delims = ",";
				char [] delim = delims.ToCharArray();
				string [] split = line.Split(delim);
				if (split.Length==0)
				{
					MessageBox.Show(strings.observednoheaders + observedDataFile,
					                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Error);
					dataFileBox.Text = "";
				    return;
				}
				observedVars = new List<string>();
				string col1 = split[0].ToLower();
				if (col1.Equals("date")) observedStart = 1;
				else observedStart = 0;
				for (int i = observedStart; i < split.Length; i++)
				{
					observedVars.Add(split[i]);
				}
				runBox.Items.Add("observed");
			}
		}
		
		void addPlotClick(object sender, EventArgs e)
		{
			if (scenariosDir.Equals(""))
			{
				MessageBox.Show(strings.needscenariosfolder, "SWATPlot",
				                MessageBoxButtons.OK,
				                MessageBoxIcon.Exclamation);
				return;
			}
			int index = dataGrid.Rows.Add();
			DataGridViewRow row = dataGrid.Rows[index];
			row.Cells[0].Value = (index+1).ToString();
			row.Cells[1].Value = "";
			row.Cells[2].Value = "";
			row.Cells[3].Value = "";
			row.Cells[4].Value = "";
			row.Cells[5].Value = "";
			dataGrid.CurrentCell = row.Cells[1];
		}
		
		void deletePlotClick(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (row != null) 
			{
				int rowIndex = dataGrid.CurrentCellAddress.Y;
				dataGrid.Rows.Remove(row);
				int count = dataGrid.Rows.Count;
				for (int i = rowIndex; i < dataGrid.Rows.Count; i++)
				{
					dataGrid.Rows[i].Cells[0].Value = (i+1).ToString();
				}
			}
		}
		

		void copyPlotClick(object sender, EventArgs e)
		{
			DataGridViewRow orow = dataGrid.CurrentRow;
			if (orow != null)
			{
				int index = dataGrid.Rows.Add();
				DataGridViewRow row = dataGrid.Rows[index];
				row.Cells[0].Value = (index+1).ToString();
				row.Cells[1].Value = orow.Cells[1].Value;
				row.Cells[2].Value = orow.Cells[2].Value;
				row.Cells[3].Value = orow.Cells[3].Value;
				row.Cells[4].Value = orow.Cells[4].Value;
				row.Cells[5].Value = orow.Cells[5].Value;
				dataGrid.CurrentCell = row.Cells[1];
			}
			
		}
		
		void moveUpClick(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (row != null)
			{
				int rowIndex = dataGrid.CurrentCellAddress.Y;
				if (rowIndex > 0)
				{
					int upRowIndex = rowIndex-1;
					DataGridViewRowCollection rows = dataGrid.Rows;
					DataGridViewRow upRow = rows[upRowIndex];
					row.Cells[0].Value = rowIndex.ToString();
					upRow.Cells[0].Value = (rowIndex+1).ToString();
					rows.Remove(upRow);
					rows.Insert(rowIndex, upRow);
				}
			}
		}
		
		void moveDownClick(object sender, EventArgs e)
		{
			
			DataGridViewRow row = dataGrid.CurrentRow;
			if (row != null)
			{
				int rowIndex = dataGrid.CurrentCellAddress.Y;
				DataGridViewRowCollection rows = dataGrid.Rows;
				int lastIndex = rows.Count - 1;
				if (rowIndex < lastIndex)
				{
					int downRowIndex = rowIndex + 1;
					DataGridViewRow downRow = rows[downRowIndex];
					row.Cells[0].Value = (downRowIndex+1).ToString();
					downRow.Cells[0].Value = downRowIndex.ToString();
					rows.Remove(downRow);
					rows.Insert(rowIndex, downRow);
				}
			}
		}
		
		void plotClick(object sender, EventArgs e)
		{
			if (dataGrid.Rows.Count == 0)
			{
				MessageBox.Show(strings.noplotdata, "SWATPlot",
				                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				return;
			}
			try
			{
				bool cioFound = false;
				int rowNum = 0;
				string cioFile = ""; // for compiler
				while (!cioFound)
				{
					DataGridViewRow row = dataGrid.Rows[rowNum];
					string run = (string) row.Cells[1].Value;
					cioFile = scenariosDir + @"\" + run + @"\TxtInOut\file.cio";
					if (System.IO.File.Exists(cioFile)) cioFound = true;
					else
					{
						rowNum++;
						if (rowNum == dataGrid.Rows.Count)
						{
							// try for Default in case only observed used
							cioFile = scenariosDir + @"\Default\TxtInOut\file.cio";
							if (System.IO.File.Exists(cioFile)) cioFound = true;
							else
							{
								MessageBox.Show(strings.nofile_cio,
								                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Error);
								return;
							}
						}
					}
				}
				string line;
				int numCols = dataGrid.Rows.Count + 1;
				string[] labels = new string[numCols];
				// get number of records
				int numYears;
				int numRecords;
				int startOffset;
				int finishOffset; // not in fact used
				using (StreamReader sr = new StreamReader(cioFile))
				{
					// skip 7 lines
					for (int i = 1; i <= 7; i++) sr.ReadLine();
					line = sr.ReadLine();
					numYears = Int32.Parse(line.Substring(0, 16));
					line = sr.ReadLine();
					startYear = Int32.Parse(line.Substring(0, 16));
					finishYear = startYear + numYears;
					line = sr.ReadLine();
					try // may be blank, so allow parse to fail
					{
						startDay = Int32.Parse(line.Substring(0, 16));
						if (startDay == 0) startDay = 1;
					}
					catch (Exception)
					{
						startDay = 1;
					}
					line = sr.ReadLine();
					try // may be blank, so allow parse to fail
					{
						finishDay = Int32.Parse(line.Substring(0, 16));
						if (finishDay == 0) finishDay = 365 + (isLeapYear(finishYear)?1:0);
					}
					catch (Exception)
					{
						finishDay = 365 + (isLeapYear(finishYear)?1:0);
					}
					// skip 47 lines, so next is line 59
					for (int i = 1; i <= 47; i++) sr.ReadLine();
					line = sr.ReadLine();
					IPRINT = Int32.Parse(line.Substring(0, 16));
					line = sr.ReadLine();
					NYSKIP = Int32.Parse(line.Substring(0, 16));
					startYear += NYSKIP;
				}
				// check against requested dates
				try
				{
					requestedStartYear = Int32.Parse(startYearBox.Text);
				}
				catch (Exception)
				{
					requestedStartYear = startYear;
					startYearBox.Text = startYear.ToString();
				}
				try
				{
					requestedStartDay = toJulianDay(Int32.Parse(startDayBox.Text), startMonthBox.Text, requestedStartYear);
				}
				catch (Exception)
				{
					requestedStartDay = startDay;
					startDayBox.Text = startDay.ToString();
				}
				try
				{
					requestedFinishYear = Int32.Parse(finishYearBox.Text);
				}
				catch (Exception)
				{
					requestedFinishYear = finishYear;
					finishYearBox.Text = finishYear.ToString();
				}
				try
				{
					requestedFinishDay = toJulianDay(Int32.Parse(finishDayBox.Text), finishMonthBox.Text, requestedFinishYear);
				}
				catch (Exception)
				{
					requestedFinishDay = finishDay;
					finishDayBox.Text = finishDay.ToString();
				}
				if ((requestedStartDay < 0) ||
				    (requestedStartYear < startYear) ||
				    ((requestedStartYear == startYear) && (requestedStartDay < startDay)))
				{
					int day;
					string month;
					int dummy;
					fromJulian(startYear, startDay, out month, out dummy, out day);
					MessageBox.Show(strings.earlieststartdate + day.ToString() + " " + month + " " + startYear.ToString(), "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Warning);
					requestedStartDay = startDay;
					requestedStartYear = startYear;
					startDayBox.Text = startDay.ToString();
					startMonthBox.Text = month;
					startYearBox.Text = startYear.ToString();
				}
				if ((requestedFinishDay < 0 ) ||
				    (requestedFinishYear > finishYear) ||
				    ((requestedFinishYear == finishYear) && (requestedFinishDay > finishDay)))
				{
					int day;
					string month;
					int dummy;
					fromJulian(finishYear, finishDay, out month, out dummy, out day);
					MessageBox.Show(strings.latestfinishdate + day.ToString() + " " + month + " " + startYear.ToString(), "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Warning);
					requestedFinishDay = finishDay;
					requestedFinishYear = finishYear;
					finishDayBox.Text = finishDay.ToString();
					finishMonthBox.Text = month;
				}
				if (IPRINT == 0)
				{
					numRecords = PeriodInMonths(requestedStartYear, requestedStartDay, requestedFinishYear, requestedFinishDay);
					startOffset = PeriodInMonths(startYear, startDay, requestedStartYear, requestedStartDay) - 1;
					finishOffset = PeriodInMonths(requestedFinishYear, requestedFinishDay, finishYear, finishDay) - 1;
					labels[0] = "Month";
				}
				else if (IPRINT == 1)
				{
					numRecords = PeriodInDays(requestedStartYear, requestedStartDay, requestedFinishYear, requestedFinishDay);
					startOffset = PeriodInDays(startYear, startDay, requestedStartYear, requestedStartDay) - 1;
					finishOffset = PeriodInDays(requestedFinishYear, requestedFinishDay, finishYear, finishDay) - 1;
					labels[0] = "Day";
				}
				else
				{
					numRecords = requestedFinishYear - requestedStartYear + 1;
					startOffset = requestedStartYear - startYear;
					finishOffset = finishYear - requestedFinishYear;
					labels[0] = "Year";
				}
				
				string[,] data = new string[numRecords, numCols];
				for (rowNum = 0; rowNum < numCols - 1; rowNum++)
				{
					DataGridViewRow nextRow = dataGrid.Rows[rowNum];
					string nextRun = (string) nextRow.Cells[1].Value;
					string nextSource = (string) nextRow.Cells[2].Value;
					string nextFile = FileForRow(nextRun, nextSource);
					string nextRecord;
					string var = (string) nextRow.Cells[5].Value;
					int skipNum = 0;
					int count = 0;
					string valstr;
					double val;
					if (nextRun.Equals("observed"))
					{
						int col = observedStart + observedVars.IndexOf(var);
						if (col - observedStart < 0)
						{
							MessageBox.Show(strings.noobservedvar1 + var + strings.noobservedvar2,
							                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
							return;
						}
						labels[rowNum+1] = nextRun + "-" + var.Trim();
						using (StreamReader sr = new StreamReader(observedDataFile))
						{
							string delims = ",";
							char [] delim = delims.ToCharArray();
							sr.ReadLine(); // skip headers
							for (int i = 0; i < startOffset; i++) sr.ReadLine(); // skip start offset
							for (int i = 0; i < numRecords; i++)
							{
								line = sr.ReadLine();
								if (line==null)
								{
									if (rowNum == 0) break; // first row used to set number of records
									MessageBox.Show(strings.insufficientrecords + observedDataFile,
									                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
									return;
								}
								string [] split = line.Split(delim);
								if (split.Length <= col)
								{
									MessageBox.Show(strings.insufficientdata1 + (i+1).ToString() +
									                strings.insufficientdata2 + observedDataFile,
									                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
									return;
								}
								valstr = split[col].Trim();
								// parse and then convert to string as it gives more readable numbers
								// than SWAT's scientific notation
								try
								{
									val = Double.Parse(valstr);
								}
								catch (Exception)
								{
									MessageBox.Show(strings.noparse + valstr,
									                "SWATPlot",
									                MessageBoxButtons.OK,
									                MessageBoxIcon.Error);
									val = -1;
								}
								data[count, rowNum+1] = val.ToString("R", CultureInfo.InvariantCulture);
								count++;
							}
						}
					}
					else // run is not observed
					{
						//we  will start actually recording data when count reaches 0
						count = 0 - startOffset;
						int nextRecordNum = 0;
						if (nextSource.Equals("hru"))
						{
							nextRecord = (string) nextRow.Cells[4].Value;  // hru number
							int hruNum = Int32.Parse(nextRecord);
							HruSkipCount(nextRun, hruNum, ref skipNum, ref nextRecordNum);
							if (skipNum == 0)
							{
								MessageBox.Show(strings.nohrus + nextRun,
								                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
								return;
							}
						}
						else if (nextSource.Equals("water"))
						{
							nextRecord = (string) nextRow.Cells[4].Value;  // hru number
							int hruNum = Int32.Parse(nextRecord);
							PondSkipCount(nextRun, hruNum, ref skipNum, ref nextRecordNum);
							if (skipNum == 0)
							{
								MessageBox.Show(strings.noponds + nextRun,
								                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
								return;
							}
						}
						else if (nextSource.Equals("reservoir"))
						{
							nextRecord = (string) nextRow.Cells[3].Value; // basin number
							int basin = Int32.Parse(nextRecord);
							nextRecordNum =  runs[nextRun].reservoirs[basin]; // reservoir number
							skipNum = runs[nextRun].reservoirs.Count; // number of reservoirs
							if (skipNum == 0)
							{
								MessageBox.Show(strings.noreservoirs + nextRun,
								                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
								return;
							}
						}
						else // reach or sub
						{
							nextRecord = (string) nextRow.Cells[3].Value; // subbasin number
							nextRecordNum = Int32.Parse(nextRecord);
							skipNum = runs[nextRun].hrus.Count; // number of subbasins
						}
						
						
						labels[rowNum+1] = nextRun + "-" + nextSource + "-" + nextRecord + "-" + var.Trim();
						int varLength;
						int varPos;
						int datePos;
						VarPos(nextSource, var, out varLength, out varPos, out datePos);
						if (nextSource.Equals("hru") || nextSource.Equals("water"))
						{
							// for output.hru and output.wtr need to shift by 1 if hrus have five digits
							int shift = runs[nextRun].fiveDigitHruShift;
							varPos += shift;
							datePos += shift;
						}
						bool finished = false;
						int currentYear = startYear;
						int currentDay = startDay;
						bool yearChecked = false;
						using (StreamReader next = new StreamReader(nextFile))
						{
							int linesToSkip = headerLines + nextRecordNum;
							for (int i = 1; i <= linesToSkip; i++) next.ReadLine();
							while (!finished)
							{
								line = next.ReadLine();
								if (line == null) finished = true;
								else
								{
									string date = line.Substring(datePos, dateWidth).Trim();
									if ((date.Length == 4) && !yearChecked)
									{
										if (!date.Equals(startYear.ToString()))
										{
											MessageBox.Show(strings.startyear + startYear.ToString() +
											                strings.from +
											                cioFile + strings.noagreestartyear + date +
											                strings.from + nextFile + ". " +
											                strings.plotwrong,
											                "SWATPlot",
											                MessageBoxButtons.OK,
											                MessageBoxIcon.Warning);
										}
										yearChecked = true;
									}
									if (((IPRINT != 2) && (date.Length == 4)) ||
									    (date.IndexOf('.') >= 0))
									{
										// skip end of year or summary lines
										for (int j = 1; j < skipNum; j++) next.ReadLine();
										line = next.ReadLine();
										if (line == null)
										{
											finished = true;
											break;
										}
										date = line.Substring(datePos, dateWidth).Trim();
										if (date.IndexOf('.') >= 0) // summary line
										{
											finished = true;
											break;
										}
									}
									// if count < 0 still skipping records before requested start datete
									if (count >= 0)
									{
										if (data[count, 0] == null) // set date field
										{
											switch (IPRINT)
											{
												case 0: // monthly: format YYYY\MM
													bool endYear = date.Equals("12");
													date = currentYear.ToString() + @"\" + date;
													if (endYear) currentYear++;
													break;
												case 1: // daily: Julian format YYYYDDD
													int lastDay = isLeapYear(currentYear)?366:365;
													date = currentYear.ToString() + currentDay.ToString("D3");
													if (currentDay == lastDay)
													{
														currentYear++;
														currentDay = 1;
													}
													else currentDay++;
													break;
												default: // IPRINT = 2: annual: format already YYYY
													break;
											}
											data[count, 0] = date;
										}
										val = -1;
										valstr = line.Substring(varPos, varLength).Trim();
										bool parseFail = false;
										// parse and then convert to string as it gives more readable numbers								// parse and then convert to string as it gives more readable numbers
										// than SWAT's scientific notation
										try
										{
											val = Double.Parse(valstr);
										}
										catch (Exception)
										{
											// bug in output.sub
											if (nextSource.Equals("subbasin") && (varPos >= 224))
											{
												// try shifted one place to right
												try
												{
													valstr = line.Substring(varPos+1, varLength).Trim();
													val = Double.Parse(valstr);
												}
												catch (Exception)
												{
													parseFail = true;
												}
											}
											// bug in output.hru
											else if (nextSource.Equals("hru") && (varPos >= 714))
											{
												//try shifted one or two places to right
												try
												{
													valstr = line.Substring(varPos+1, varLength).Trim();
													val = Double.Parse(valstr);
												}
												catch (Exception)
												{
													try
													{
														valstr = line.Substring(varPos+2, varLength).Trim();
														val = Double.Parse(valstr);
													}
													catch (Exception)
													{
														parseFail = true;
													}
												}
											}
											else
												parseFail = true;
										}
										if (parseFail)
										{
											MessageBox.Show(strings.noparse + valstr,
											                "SWATPlot",
											                MessageBoxButtons.OK,
											                MessageBoxIcon.Error);
										}
										data[count, rowNum+1] = val.ToString("R", CultureInfo.InvariantCulture);
									}
									count++;
									if (count == numRecords) finished = true;
									for (int j = 1; j < skipNum; j++) next.ReadLine();
								}
							}
						}
						if (count < numRecords)
						{
							MessageBox.Show(strings.insufficientrecords + nextFile,
							                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
							numRecords = count;
						}
					}
				}
				// write plot data
				
				plotFileDialog.InitialDirectory = scenariosDir;
				if (plotFileDialog.ShowDialog() == DialogResult.OK)
				{
					string plotFile = plotFileDialog.FileName;
					
					if (System.IO.File.Exists(plotFile)) System.IO.File.Delete(plotFile);
					using (StreamWriter w = new StreamWriter(plotFile))
					{
						//w.Write(numMonths.ToString());
						//w.Write(",");
						//w.WriteLine(numCols.ToString());
						int lastCol = numCols - 1;
						for (int i = 0; i < lastCol; i++)
						{
							w.Write(labels[i]);
							w.Write(",");
						}
						w.WriteLine(labels[lastCol]);
						for (int i = 0; i < numRecords; i++)
						{
							for (int j = 0; j < lastCol; j++)
							{
								w.Write(data[i,j]);
								w.Write(",");
							}
							w.WriteLine(data[i,lastCol]);
						}
					}
					//MessageBox.Show("Plot data written to " + plotFile, "SWATPlot",
					//                MessageBoxButtons.OK, MessageBoxIcon.Information);
					System.Diagnostics.Process graphTool = new System.Diagnostics.Process();
					graphTool.StartInfo.CreateNoWindow = false;
					graphTool.StartInfo.Arguments = "\"" + plotFile + "\"";
					//graphTool.StartInfo.WorkingDirectory =
					graphTool.StartInfo.FileName =
						appDir + @"\SWATGraph.exe";
					if (System.IO.File.Exists(graphTool.StartInfo.FileName))
					{
						//MessageBox.Show("Starting graph tool " + graphTool.StartInfo.FileName
						//               + " with parameter " + graphTool.StartInfo.Arguments);
						graphTool.Start();
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(strings.cannotwrite1 + ex.Message +
				                strings.cannotwrite2, "SWATPlot",
				                MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		
		string FileForRow(string run, string source)
		{
			if (run.Equals("observed")) return observedDataFile;
			string dir = scenariosDir + @"\" + run + @"\TxtInOut\";
			switch (source)
			{
				case "reach":
					return dir + reachDataFile;
				case "subbasin":
					return dir + subDataFile;
				case "hru":
					return dir + hruDataFile;
				case "water":
					return dir + waterDataFile;
				case "reservoir":
					return dir + reservoirDataFile;
				default: // incomplete row
					return "";
			}
		}
		
		void VarPos(string source, string var, out int length, out int pos, out int datePos)
		{
			int index = 0;
			length = 0;
			pos = 0;
			datePos = 0;
			switch (source)
			{
				case "reach":
					index = reachVars.IndexOf(var);
					length = reachDataWidth;
					pos = reachFirstDataCol + index * reachDataWidth;
					datePos = reachDateCol;
					return;
				case "subbasin":
					index = subVars.IndexOf(var);
					length = subDataWidth;
					pos = subFirstDataCol + index * subDataWidth;
					datePos = subDateCol;
					return;
				case "hru":
					index = hruVars.IndexOf(var);
					length = hruDataWidth;
					pos = hruFirstDataCol + index * hruDataWidth;
					datePos = hruDateCol;
					return;
				case "water":
					index = waterVars.IndexOf(var);
					length = waterDataWidth;
					pos = waterFirstDataCol + index * waterDataWidth;
					datePos = waterDateCol;
					return;
				case "reservoir":
					index = reservoirVars.IndexOf(var);
					length = reservoirDataWidth;
					pos = reservoirFirstDataCol + index * reservoirDataWidth;
					datePos = reservoirDateCol;
					return;
				default:
					return;
			}
		}
		
		void cancelClick(object sender, EventArgs e)
		{
			if (System.IO.Directory.Exists(scenariosDir))
			{
				System.Xml.XmlDocument doc = new XmlDocument();
				doc.LoadXml("<SWATPlot/>");
				System.Xml.XmlElement scenarios = doc.CreateElement("Scenarios");
				doc.DocumentElement.AppendChild(scenarios);
				System.Xml.XmlElement scenario = doc.CreateElement("Scenario");
				System.Xml.XmlAttribute pathAtt = doc.CreateAttribute("Path");
				pathAtt.Value = scenariosDir;
				scenario.Attributes.Append(pathAtt);
				System.Xml.XmlAttribute obsAtt = doc.CreateAttribute("Observed");
				if (scenObs.ContainsKey(scenariosDir)) obsAtt.Value = scenObs[scenariosDir];
				else obsAtt.Value = "";
				scenario.Attributes.Append(obsAtt);
				doc.DocumentElement.FirstChild.AppendChild(scenario);
				int index = scenariosComboBox.Items.IndexOf(scenariosDir);
				if (index >= 0) scenariosComboBox.Items.RemoveAt(index);
				// store up to 10; 1 done already
				int todo = Math.Min(9, scenariosComboBox.Items.Count);
				for (int i = 0; i < todo; i++)
				{
					string scenDir = (string) scenariosComboBox.Items[i];
					if (System.IO.Directory.Exists(scenDir))
					{
						scenario = doc.CreateElement("Scenario");
						pathAtt = doc.CreateAttribute("Path");
						pathAtt.Value = scenDir;
						scenario.Attributes.Append(pathAtt);
						obsAtt = doc.CreateAttribute("Observed");
						if (scenObs.ContainsKey(scenDir)) obsAtt.Value = scenObs[scenDir];
						else obsAtt.Value = "";
						scenario.Attributes.Append(obsAtt);
						doc.DocumentElement.FirstChild.AppendChild(scenario);
					}
				}
				string configPath = configDir + @"\" + configFile;
				// ignore failure, as can load without reading this file
				//try
				//{
					doc.Save(configPath);
				//}
				//catch (Exception)
				//{
				//}
				this.Close();
			}
		}
			
		void PopulateNumsAndVars(string run, string source)
		{
			if ((run != null) && (run.Equals("observed") || runs.ContainsKey(run))) // in case not set yet
			{
				DataGridViewRow row = dataGrid.CurrentRow;
				subBox.Items.Clear();
				hruBox.Items.Clear();
				varBox.Items.Clear();
				if (runs.ContainsKey(run)) // ie not observed
				{
					Basins basins = runs[run];
					if (source.Equals("reservoir"))
						foreach (KeyValuePair<int, int> kvp in runs[run].reservoirs)
					{
						subBox.Items.Add(kvp.Key.ToString());
					}
					else
					{
						int maxBasin = basins.hrus.Count;
						for (int i = 1; i <= maxBasin; i++)
						{
							if (source.Equals("water"))
							{
								if (runs[run].hrus[i].pondNumbers.Count > 0) subBox.Items.Add(i.ToString());
							}
							else if (source.Equals("hru"))
							{
								// only add if we have some hrus in this basin
								if (runs[run].hrus[i].hruNumbers.Count > 0) subBox.Items.Add(i.ToString());
							}
							else subBox.Items.Add(i.ToString());
						}
					}
					switch (source)
					{
						case "reach":
							for (int i = 0; i < reachVars.Count; i++)
							{
								varBox.Items.Add(reachVars[i]);
							}
							hruBox.Items.Add("-");
							hruBox.Text = "";
							if (row != null) row.Cells[4].Value = "-";
							break;
						case "subbasin":
							for (int i = 0; i < subVars.Count; i++)
							{
								varBox.Items.Add(subVars[i]);
							}
							hruBox.Items.Add("-");
							hruBox.Text = "";
							if (row != null) row.Cells[4].Value = "-";
							break;
						case "hru":
							for (int i = 0; i < hruVars.Count; i++)
							{
								varBox.Items.Add(hruVars[i]);
							}
							if (!(row == null))
							{
								if (row.Cells[2].Value.Equals("hru"))
								{
									try
									{
										int basin = Int32.Parse((string) row.Cells[3].Value);
										Hrus hrus = basins.hrus[basin];
										if (hrus.hruNumbers.Count == 0)
										{
											hruBox.Items.Add("-");
											hruBox.Text = "";
											row.Cells[4].Value = "-";
										}
										else
										{
											foreach (int i in hrus.hruNumbers)
											{
												hruBox.Items.Add(i.ToString());
											}
										}
									}
									catch (Exception) {}
								}
							}
							break;
						case "water":
							for (int i = 0; i < waterVars.Count; i++)
							{
								varBox.Items.Add(waterVars[i]);
							}
							if (!(row == null))
							{
								if (row.Cells[2].Value.Equals("water"))
								{
									try
									{
										int basin = Int32.Parse((string) row.Cells[3].Value);
										Hrus hrus = basins.hrus[basin];
										foreach (int i in hrus.pondNumbers)
										{
											hruBox.Items.Add(i.ToString());
										}
									}
									catch (Exception) {}
								}
							}
							break;
						case "reservoir":
							for (int i = 0; i < reservoirVars.Count; i++)
							{
								varBox.Items.Add(reservoirVars[i]);
							}
							hruBox.Items.Add("-");
							hruBox.Text = "";
							if (row != null) row.Cells[4].Value = "-";
							break;
						default: // in case source not set yet
							break;
					}
				}
				else // observed
				{
					sourceBox.Items.Clear();
					sourceBox.Items.Add("-");
					if (row != null) row.Cells[2].Value = "-";
					subBox.Items.Add("-");
					if (row != null) row.Cells[3].Value = "-";
					hruBox.Items.Add("-");
					if (row != null) row.Cells[4].Value = "-";
					for (int i = 0; i < observedVars.Count; i++)
					{
						varBox.Items.Add(observedVars[i]);
					}
				}
			}
		}
		
		void SetSources(string run)
		{
			sourceBox.Items.Clear();
			sourceBox.Items.Add("reach");
			sourceBox.Items.Add("subbasin");
			sourceBox.Items.Add("hru");
			if (hasPonds(run)) sourceBox.Items.Add("water");
			if ((runs==null) || !runs.ContainsKey(run)) return;
			if (runs[run].reservoirs.Count > 0) sourceBox.Items.Add("reservoir");
		}
		
		void CheckRow(DataGridViewRow row)
		{
			DataGridViewCellCollection cells = row.Cells;
			if ((cells[1].Value == null) || 
			    !runBox.Items.Contains(cells[1].Value)) cells[1].Value = "";
			if ((cells[2].Value == null) ||
			    !sourceBox.Items.Contains(cells[2].Value)) cells[2].Value = "";
			if ((cells[3].Value == null) ||
			    !subBox.Items.Contains(cells[3].Value)) cells[3].Value = "";
			if ((cells[4].Value == null) || 
			    !hruBox.Items.Contains(cells[4].Value))
			{
				cells[4].Value = "";
				hruBox.Text = "";	
			}
			if ((cells[5].Value == null) ||
			    !varBox.Items.Contains(cells[5].Value)) 
			{
				cells[5].Value = "";
				varBox.Text = "";
			}
		}
		
		void runItemChanged(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (row == null) 
			{
				addPlotClick(sender, e);
				row = dataGrid.CurrentRow;
			}
			if (row != null)
			{
				DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell) row.Cells[1];
				cell.Value = (string) runBox.SelectedItem;
				if (runBox.SelectedItem.Equals("observed"))
					PopulateNumsAndVars("observed", "");
				else SetSources((string) runBox.SelectedItem);
			}
		}
		
		void sourceItemChanged(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (!(row == null))
			{
				string run = (string) row.Cells[1].Value;
				if ((run == null) || run.Equals("")) return;
				// prevent recursive call of PopulateNumsAndVars
				if (!run.Equals("observed"))
				{ 
					string source = (string) sourceBox.SelectedItem;
					DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell) row.Cells[2];
					cell.Value = source;
					PopulateNumsAndVars((string) row.Cells[1].Value, source);
					CheckRow(row);
				}
			}
		}
		
		void subTextChanged(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (!(row == null))
			{
				
				DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell) row.Cells[3];
				cell.Value = (string) subBox.Text;
				if ((row.Cells[1].Value != null) &&
				    (row.Cells[2].Value != null) &&
				    (row.Cells[2].Value.Equals("hru") || row.Cells[2].Value.Equals("water")))
				{
					try
					{
						Basins basins = runs[(string) row.Cells[1].Value];
						int basin = Int32.Parse((string) cell.Value);
						Hrus hrus = basins.hrus[basin];
						hruBox.Items.Clear();
						hruBox.Text = "";
						if (row.Cells[2].Value.Equals("hru"))
							foreach (int i in hrus.hruNumbers)
						{
							hruBox.Items.Add(i.ToString());
						}
						else
							foreach (int i in hrus.pondNumbers)
						{
							hruBox.Items.Add(i.ToString());
						}
						
						if ((row.Cells[4] == null) ||
						    !hruBox.Items.Contains(row.Cells[4].Value))
						{
							row.Cells[4].Value = "";
							hruBox.Text = "";
						}
					}
					catch (Exception) {}
				}
			}
		}
		
		void subItemChanged(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (!(row == null))
			{
				DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell) row.Cells[3];
				cell.Value = (string) subBox.SelectedItem;
				if ((row.Cells[1].Value != null) &&
				    (row.Cells[2].Value != null) &&
				    (row.Cells[2].Value.Equals("hru") || row.Cells[2].Value.Equals("water")))
				{
					try
					{
						Basins basins = runs[(string) row.Cells[1].Value];
						int basin = Int32.Parse((string) cell.Value);
						Hrus hrus = basins.hrus[basin];
						hruBox.Items.Clear();
						hruBox.Text = "";
						if (row.Cells[2].Value.Equals("hru"))
							foreach (int i in hrus.hruNumbers)
						{
							hruBox.Items.Add(i.ToString());
						}
						else
							foreach (int i in hrus.pondNumbers)
						{
							hruBox.Items.Add(i.ToString());
						}
						if ((row.Cells[4] == null) ||
						    !hruBox.Items.Contains(row.Cells[4].Value))
						{
							row.Cells[4].Value = "";
							hruBox.Text = "";
						}
					}
					catch (Exception) {}
				}
			}
		}
		
		void hruItemChanged(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (!(row == null))
			{
				DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell) row.Cells[4];
				cell.Value = (string) hruBox.SelectedItem;
			}
		}
		
		void varItemChanged(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (!(row == null))
			{
				DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell) row.Cells[5];
				cell.Value = (string) varBox.SelectedItem;
			}
		}

		void selectionChanged(object sender, EventArgs e)
		{
			DataGridViewRow row = dataGrid.CurrentRow;
			if (!(row == null))
			{
				string run = (string) row.Cells[1].Value;
				string source = (string) row.Cells[2].Value;
				PopulateNumsAndVars(run, source);
			}
		}
		
		bool PopulateBasins(ref Basins basins, string dirName)
		{
			string dir = scenariosDir + @"\" + dirName;
			string hruData = dir + @"\TxtInOut\" + hruDataFile;
			string reachData = dir + @"\TxtInOut\" + reachDataFile;
			string figData = dir + @"\TxtInOut\" + figFile;
			string line;
			try
			{
				// we should be using rch.dat, sub.dat and hru.dat for the field widths and 
				// variables to be found in output.rch etc, but rch.dat at least is unreliable.
				// So we assume we know the field positions and widths, and read the variables from the
				// output files.
				if (dirName.ToLower().Equals("default"))
				{
					using (StreamReader sr1 = new StreamReader(reachData))
					{
						reachVars = new List<string>();
						// check if SWAT 2012
						sr1.ReadLine();
						line = sr1.ReadLine();
						if (line.Contains("2012"))
						{
							// SWAT 2012
							// HRU column in output.wtr and output.hru has an extra space before and after it
							waterFirstDataCol = 44;
							waterNumCol = 5;
							waterDateCol = 30;
							waterSubNumCol = 19;
							hruFirstDataCol = 44;
							hruNumCol = 5;
							hruDateCol = 30;
							hruSubNumCol = 19;
						}
						// skip remaining header lines
						for (int i = 0; i < headerLines - 2; i++) sr1.ReadLine();
						line = sr1.ReadLine();
						int count = (line.Length - reachFirstDataCol)/reachDataWidth;
						for (int i = 0; i < count; i++)
						{
							reachVars.Add(line.Substring(reachFirstDataCol + i*reachDataWidth, reachDataWidth));
						}
					}
					string subData = dir + @"\TxtInOut\" + subDataFile;
					using (StreamReader sr2 = new StreamReader(subData))
					{
						subVars = new List<string>();
						// skip header lines
						for (int i = 0; i < headerLines; i++) sr2.ReadLine();
						line = sr2.ReadLine();
						int count = (line.Length - subFirstDataCol)/subDataWidth;
						for (int i = 0; i < count; i++)
						{
							subVars.Add(line.Substring(subFirstDataCol + i*subDataWidth, subDataWidth));
						}
					}
					string waterData = dir + @"\TxtInOut\" + waterDataFile;
					if (File.Exists(waterData))
					{
						using (StreamReader sr3 = new StreamReader(waterData))
						{
							waterVars = new List<string>();
							// skip header lines
							for (int i = 0; i < headerLines; i++) sr3.ReadLine();
							line = sr3.ReadLine();
							int count = (line.Length - waterFirstDataCol)/waterDataWidth;
							for (int i = 0; i < count; i++)
							{
								waterVars.Add(line.Substring(waterFirstDataCol + i*waterDataWidth, waterDataWidth));
							}
						}
					}
					string reservoirData = dir + @"\TxtInOut\" + reservoirDataFile;
					using (StreamReader sr4 = new StreamReader(reservoirData))
					{
						reservoirVars = new List<string>();
						// skip header lines
						for (int i = 0; i < headerLines; i++) sr4.ReadLine();
						line = sr4.ReadLine();
						int count = (line.Length - reservoirFirstDataCol)/reservoirDataWidth;
						for (int i = 0; i < count; i++)
						{
							reservoirVars.Add(line.Substring(reservoirFirstDataCol + i*reservoirDataWidth, reservoirDataWidth));
						}
					}
				} 
				// use output.hru to determine hru numbers for each basin
				// Note we do this for the current, not Default, run since different runs may have different hru selections
				using (StreamReader sr = new StreamReader(hruData))
				{
					// skip header lines
					for (int i = 0; i < headerLines; i++) sr.ReadLine();
					// next is var names line
					line = sr.ReadLine();
					if (dirName.ToLower().Equals("default"))
					{ // store hru variables
						hruVars = new List<string>();
						int count = (line.Length - hruFirstDataCol)/hruDataWidth;
						for (int i = 0; i < count; i++)
						{
							hruVars.Add(line.Substring(hruFirstDataCol + i*hruDataWidth, hruDataWidth));
						}
					}
					int currentBasin = 0; // basin numbers start from 1
					List<int> hruNums = new List<int>();
					while ((line = sr.ReadLine()) != null)
					{
						// first time round, check for change in output.hru in latest
						// swat2005.exe: hrus allowed 5 digits, so following numbers shifted 1 place to right
						// detected by seeing if last character of 5-digit hru number is a non-space 
						// (should be a 1 if hrus have 5 digits)
						if ((currentBasin == 0) && (line.Substring(hruNumCol + hruNumWidth, 1) != " "))
						{
							basins.fiveDigitHruShift = 1;
						}
						int basin = Int32.Parse(line.Substring(hruSubNumCol + basins.fiveDigitHruShift, hruSubNumWidth));
						int hru = Int32.Parse(line.Substring(hruNumCol, hruNumWidth + basins.fiveDigitHruShift));
						if (basin == currentBasin)
						{
							if (!hruNums.Contains(hru)) hruNums.Add(hru);
						}
						else
						{
							// store last basin, unless this is the first loop cycle
							if (currentBasin != 0)
							{
								Hrus hrus = new Hrus();
								hrus.hruNumbers = new List<int>();
								hrus.pondNumbers = new List<int>();
								foreach (int i in hruNums) hrus.hruNumbers.Add(i);
								basins.hrus.Add(currentBasin, hrus);
							}
							if (basin < currentBasin) break; // finished with hrus
							// start a new basin
							currentBasin = basin;
							hruNums.Clear();
							hruNums.Add(hru);
						}
					}
					// may have curtailed hru file: check to see if we got the last entry
					if ((currentBasin > 0) && (!basins.hrus.ContainsKey(currentBasin)))
					{
						Hrus hrus = new Hrus();
						hrus.hruNumbers = new List<int>();
						hrus.pondNumbers = new List<int>();
						foreach (int i in hruNums) hrus.hruNumbers.Add(i);
						basins.hrus.Add(currentBasin, hrus);
					}
				}
				// hru data can be curtailed to just some hrus, so we have to read
				// the reach data file to make sure we have all subbasins
				using (StreamReader sr7 = new StreamReader(reachData))
				{
					// skip header lines and var line
					for (int i = 0; i <= headerLines; i++) sr7.ReadLine();
					int currentBasin = 0; // basin numbers start from 1
					while ((line = sr7.ReadLine()) != null)
					{
						int basin = Int32.Parse(line.Substring(reachNumCol, reachNumWidth));
						if (currentBasin != 0)
						{
							if (!basins.hrus.ContainsKey(currentBasin))
								// new basin that was not in output.hru
							{
								Hrus hrus = new Hrus();
								hrus.hruNumbers = new List<int>();
								hrus.pondNumbers = new List<int>();
								basins.hrus.Add(currentBasin, hrus);
							}
						}
						if (basin < currentBasin) break; // finished with reach file
						currentBasin = basin;
					}
				}
						
				string waterData2 = dir + @"\TxtInOut\" + waterDataFile;
				if (File.Exists(waterData2))
				{
					using (StreamReader sr5 = new StreamReader(waterData2))
					{
						// skip header lines and var line
						for (int i = 0; i <= headerLines; i++) sr5.ReadLine();
						int currentBasin = 0; // basin numbers start from 1
						List<int> pondNums = new List<int>();
						int basin = 0;
						int hru = 0;
						while ((line = sr5.ReadLine()) != null)
						{
							basin = Int32.Parse(line.Substring(waterSubNumCol + basins.fiveDigitHruShift, waterSubNumWidth));
							hru = Int32.Parse(line.Substring(waterNumCol, waterNumWidth + basins.fiveDigitHruShift));
							if (basin == currentBasin)
							{
								if (!pondNums.Contains(hru)) pondNums.Add(hru);
							}
							else
							{
								// store last basin, unless first loop
								if (currentBasin != 0) // not first loop cycle
								{
									foreach (int i in pondNums)
									{
										basins.hrus[currentBasin].pondNumbers.Add(i);
									}
								}
								if (basin < currentBasin) break; // finished with ponds
								currentBasin = basin;
								pondNums.Clear();
								pondNums.Add(hru);
							}
						}
					}
				}
				// use fig.fig file to set up reservoir data
				using (StreamReader sr6 = new StreamReader(figData))
				{
					int lastBasin = 0;
					while ((line = sr6.ReadLine()) != null)
					{
						int command;
						try
						{
							command = Int32.Parse(line.Substring(figCommandCol, figCommandWidth));
						}
						catch (Exception)
						{
							continue;
						}
						if (command == 2) // route command
						{
							// save basin in case next command is a reservoir routing
							lastBasin = Int32.Parse(line.Substring(figRouteBasinCol, figRouteBasinWidth));
						}
						else if (command == 3) // routres command
						{
							int res = Int32.Parse(line.Substring(figResCol, figResWidth));
							// basin removed from routres in SWAT 2012
							//int basin = Int32.Parse(line.Substring(figResBasinCol, figResBasinWidth));
							basins.reservoirs.Add(lastBasin, res);
							//MessageBox.Show("Run " + dirName + " has reservoir " + res.ToString() + 
							//                " in basin " + lastBasin.ToString());
						}
					}
				}
				return true;
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message,
				                "SWATPlot", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return false;
			}
		}
		
		/// <summary>
		/// Initialises start/finish dates from Default\files.cio
		/// </summary>
		private void InitialiseDates()
		{
			try {
				string cioFile =  scenariosDir + @"\Default\TxtInOut\file.cio";
				if (System.IO.File.Exists(cioFile))
					using (StreamReader sr = new StreamReader(cioFile))
				{
					// skip 7 lines
					for (int i = 1; i <= 7; i++) sr.ReadLine();
					string line = sr.ReadLine();
					int nbyr = Int32.Parse(line.Substring(0, 16));
					line = sr.ReadLine();
					requestedStartYear = Int32.Parse(line.Substring(0, 16));
					requestedFinishYear = requestedStartYear + nbyr - 1;
					line = sr.ReadLine();
					try // may be blank, so allow parse to fail
					{
						requestedStartDay = Int32.Parse(line.Substring(0, 16));
						if (requestedStartDay == 0) requestedStartDay = 1;
					}
					catch (Exception)
					{
						requestedStartDay = 1;
					}
					line = sr.ReadLine();
					try // may be blank, so allow parse to fail
					{
						requestedFinishDay = Int32.Parse(line.Substring(0, 16));
						if (requestedFinishDay == 0) requestedFinishDay = 365 + (isLeapYear(requestedFinishYear)?1:0);
					}
					catch (Exception)
					{
						requestedFinishDay = 365 + (isLeapYear(requestedFinishYear)?1:0);
					}
					// skip 48 lines, so next is line 60
					for (int i = 1; i <= 48; i++) sr.ReadLine();
					line = sr.ReadLine();
					int nyskip = Int32.Parse(line.Substring(0, 16));
					requestedStartYear += nyskip;
					int monthNum;
					string month;
					int day;
					fromJulian(requestedStartYear, requestedStartDay, out month, out monthNum, out day);
					startDayBox.Text = day.ToString();
					startMonthBox.Text = month;
					startYearBox.Text = requestedStartYear.ToString();
					fromJulian(requestedFinishYear, requestedFinishDay, out month, out monthNum, out day);
					finishDayBox.Text = day.ToString();
					finishMonthBox.Text = month;
					finishYearBox.Text = requestedFinishYear.ToString();
				}
				
			} catch (Exception) {
				// do nothing - leave blank
			}
		}
		
		/// <summary>
		/// Calculate period in months from start to finish Julian dates
		/// </summary>
		/// <param name="startYear"></param>
		/// <param name="startDay"></param>
		/// <param name="finishYear"></param>
		/// <param name="finishDay"></param>
		/// <returns></returns>
		private int PeriodInMonths(int startYear, int startDay, int finishYear, int finishDay)
		{
			int startMonth;
			int finishMonth;
			string dummyMonth;
			int dummyDay;
			fromJulian(startYear, startDay, out dummyMonth, out startMonth, out dummyDay);
			fromJulian(finishYear, finishDay, out dummyMonth, out finishMonth, out dummyDay);
			if (startYear < finishYear)
			{
				// months in first year
				int result = 13 - startMonth;
				// add months in intermediate years
				int currYear = startYear + 1;
				while (currYear < finishYear)
				{
					result += 12;
					currYear++;
				}
				// add months in final year
				result += finishMonth;
				return result;
			}
			else if (startYear == finishYear)
				if (finishMonth >= startMonth)
					return finishMonth - startMonth + 1;
				else return 0;
			else return 0;
		}
		
		/// <summary>
		/// Calculate period in days from start to finish Julian dates
		/// </summary>
		/// <param name="startYear"></param>
		/// <param name="startDay"></param>
		/// <param name="finishDay"></param>
		/// <param name="finishYear"></param>
		/// <returns></returns>
		private int PeriodInDays(int startYear, int startDay, int finishYear, int finishDay)
		{
			if (startYear < finishYear)
			{
				// days in first year
				int result = 366 + (isLeapYear(startYear)?1:0) - startDay;
				// add days in intermediate years
				int currYear = startYear + 1;
				while (currYear < finishYear)
				{
					result += 365 + (isLeapYear(currYear)?1:0);
					currYear++;
				}
				// add days in final year
				result += finishDay;
				return result;
			}
			else if (startYear == finishYear)
				if (finishDay >= startDay)
					return finishDay - startDay + 1;
				else return 0;
			else return 0;
		}
		
		private static bool isLeapYear(int yr)
		{
			if (yr%4 == 0)
			{
				if (yr%100 == 0) return (yr%400 == 0);
				else return true;
			}
			else return false;
		}
		
		/// <summary>
		/// Convert day, month string and year to Julian day within that year
		/// </summary>
		/// <param name="day"></param>
		/// <param name="month"></param>
		/// <param name="year"></param>
		/// <returns></returns>
		public static int toJulianDay(int day, string month, int year)
		{
			if (string.IsNullOrEmpty(month)) return -1;
			int leapAdjust = isLeapYear(year)?1:0;
			if (month.Equals(strings.January)) return day;
			if (month.Equals(strings.February)) return 31 + day;
			if (month.Equals(strings.March)) return 59 + day + leapAdjust;
			if (month.Equals(strings.April)) return 90 + day + leapAdjust;
			if (month.Equals(strings.May)) return 120 + day + leapAdjust;
			if (month.Equals(strings.June)) return 151 + day + leapAdjust;
			if (month.Equals(strings.July)) return 181 + day + leapAdjust;
			if (month.Equals(strings.August)) return 212 + day + leapAdjust;
			if (month.Equals(strings.September)) return 243 + day + leapAdjust;
			if (month.Equals(strings.October)) return 273 + day + leapAdjust;
			if (month.Equals(strings.November)) return 304 + day + leapAdjust;
			if (month.Equals(strings.December)) return 334 + day + leapAdjust;
			return -1;
		}
		
		/// <summary>
		/// Convert date from Julian to md
		/// </summary>
		/// <param name="year">year as int</param>
		/// <param name="julian">Julian day in year as int</param>
		/// <param name="month">output month as string</param>
		/// <param name="monNum">output month as int</param>
		/// <param name="day">output day in month as int</param>
		private static void fromJulian(int year, int julian, out string month, out int monNum, out int day)
		{
			int leapAdjust = isLeapYear(year)?1:0;
			if (julian > 334 + leapAdjust)
			{
				month = strings.December;
				monNum = 12;
				day = julian - (334 + leapAdjust);
			}
			else if (julian > 304 + leapAdjust)
			{
				month = strings.November;
				monNum = 11;
				day = julian - (304 + leapAdjust);
			}
			else if (julian > 273 + leapAdjust)
			{
				month = strings.October;
				monNum = 10;
				day = julian - (273 + leapAdjust);
			}
			else if (julian > 243+ leapAdjust)
			{
				month = strings.September;
				monNum = 9;
				day = julian - (243 + leapAdjust);
			}
			else if (julian > 212 + leapAdjust)
			{
				month = strings.August;
				monNum = 8;
				day = julian - (212 + leapAdjust);
			}
			else if (julian > 181 + leapAdjust)
			{
				month = strings.July;
				monNum = 7;
				day = julian - (181 + leapAdjust);
			}
			else if (julian > 151 + leapAdjust)
			{
				month = strings.June;
				monNum = 6;
				day = julian - (151 + leapAdjust);
			}
			else if (julian > 120 + leapAdjust)
			{
				month = strings.May;
				monNum = 5;
				day = julian - (120 + leapAdjust);
			}
			else if (julian > 90 + leapAdjust)
			{
				month = strings.April;
				monNum = 4;
				day = julian - (90 + leapAdjust);
			}
			else if (julian > 59 + leapAdjust)
			{
				month = strings.March;
				monNum = 3;
				day = julian - (59 + leapAdjust);
			}
			else if (julian > 31)
			{
				month = strings.February;
				monNum = 2;
				day = julian - 31;
			}
			else
			{
				month = strings.January;
				monNum = 1;
				day = julian;
			}
		}

		
		private bool hasPonds(string run)
		{
			bool found = false;
			if ((runs == null) || !runs.ContainsKey(run)) return false;
			Basins basins = runs[run];
			foreach (KeyValuePair<int, Hrus> kvp in basins.hrus)
			{
				Hrus hrus = kvp.Value;
				if (hrus.pondNumbers.Count > 0)
				{
					found = true;
					break;
				}
			}
			return found;
		}
	
		/// <summary>
		/// Find number of lines to skip after header to first record in output.hru
		/// and number of lines to skip between records in output.hru
		/// </summary>
		/// <param name="run"></param>
		/// <param name="hru"></param>
		/// <param name="skipNum">number of lines to skip between pond records</param>
		/// <param name="nextRecordNum">number of line to skip after header</param>
		private void HruSkipCount(string run, int hru, ref int skipNum, ref int nextRecordNum)
		{
			Basins basins = runs[run];
			foreach (KeyValuePair<int, Hrus> kvp in basins.hrus)
			{
				Hrus hrus = kvp.Value;
				int indx = hrus.hruNumbers.IndexOf(hru); // assumes list is sorted
				if (indx >= 0)
					nextRecordNum = skipNum + indx + 1;
				skipNum = skipNum + hrus.hruNumbers.Count;
			}
		}
	
		/// <summary>
		/// Find number of lines to skip after header to first record in output.wtr
		/// and number of lines to skip between records in output.wtr
		/// </summary>
		/// <param name="run"></param>
		/// <param name="hru"></param>
		/// <param name="skipNum">number of lines to skip between pond records</param>
		/// <param name="nextRecordNum">number of line to skip after header</param>
		private void PondSkipCount(string run, int hru, ref int skipNum, ref int nextRecordNum)
		{
			Basins basins = runs[run];
			foreach (KeyValuePair<int, Hrus> kvp in basins.hrus)
			{
				Hrus hrus = kvp.Value;
				int indx = hrus.pondNumbers.IndexOf(hru); // assumes list is sorted
				if (indx >= 0)
					nextRecordNum = skipNum + indx + 1;
				skipNum = skipNum + hrus.pondNumbers.Count;
			}
		}
			
		/// <summary>
		/// Puts (localizable) months of year into a combo box's list
		/// </summary>
		/// <param name="box"></param>
		private static void PutMonths(System.Windows.Forms.ComboBox box)
		{
			if (box == null) return;
			box.Items.Add(strings.January);
			box.Items.Add(strings.February);
			box.Items.Add(strings.March);
			box.Items.Add(strings.April);
			box.Items.Add(strings.May);
			box.Items.Add(strings.June);
			box.Items.Add(strings.July);
			box.Items.Add(strings.August);
			box.Items.Add(strings.September);
			box.Items.Add(strings.October);
			box.Items.Add(strings.November);
			box.Items.Add(strings.December);
		}
	}
		
	/// <summary>
	/// Stores the range of hru numbers associated with a subbasin
	/// Also records if a subbasin has a pond and if it has a reservoir
	/// </summary>
	public struct Hrus
	{
		// the following two lists are assumed to be sorted
		public List<int> hruNumbers; // hrus in output.hru
		public List<int> pondNumbers; // hrus in output.wtr
	}
	
	/// <summary>
	/// Maps from basin number to hru numbers, pond  numbers and reservoir numbers
	/// Newest SWAT2005.exe uses 5 digit hru numbers, which changes positions
	/// in output.hru, so we record this.
	/// </summary>
	public struct Basins
	{
		public Dictionary<int, Hrus> hrus; // mapping from basin to hru numbers and pond numbers
		public Dictionary<int, int> reservoirs; // mapping from basin number to reservoir number (only 
		// for basins with reservoirs)
		public int fiveDigitHruShift; // set to 1 if hru numbers in output.hru are 5 digits, else 0
	}

}
